#include "Nano100Series.h"
#include "Common.h"
#include "BrgHdmiMipi.h"
#include "Edid.h"
#include "I2c0.h"

#define BRG_HDMI_MIPI_I2C_ADDR	0x0F

int BrgHdmiMipiI2cWrite8(uint16_t reg_addr, uint8_t reg_val)
{
	struct i2c_msg_t i2c_msg;
	int ret;
	uint8_t buf[3];

	buf[0] = reg_addr >> 8;
	buf[1] = reg_addr;
	buf[2] = reg_val;

	i2c_msg.addr = BRG_HDMI_MIPI_I2C_ADDR;
	i2c_msg.tx_buf = buf;
	i2c_msg.tx_to_write = 3;
	i2c_msg.tx_write = 0;
	i2c_msg.rx_buf = NULL;
	i2c_msg.rx_to_read = 0;
	i2c_msg.rx_read = 0;

	ret = I2c0Xfer(&i2c_msg);
	if (ret < 0) {
		return 0;
	} else {
		return i2c_msg.tx_write;
	}
}

int BrgHdmiMipiI2cWrite16(uint16_t reg_addr, uint16_t reg_val)
{
	struct i2c_msg_t i2c_msg;
	int ret;
	uint8_t buf[4];

	buf[0] = reg_addr >> 8;
	buf[1] = reg_addr;
	buf[2] = reg_val;
	buf[3] = reg_val >> 8;

	i2c_msg.addr = BRG_HDMI_MIPI_I2C_ADDR;
	i2c_msg.tx_buf = buf;
	i2c_msg.tx_to_write = 4;
	i2c_msg.tx_write = 0;
	i2c_msg.rx_buf = NULL;
	i2c_msg.rx_to_read = 0;
	i2c_msg.rx_read = 0;

	ret = I2c0Xfer(&i2c_msg);
	if (ret < 0) {
		return 0;
	} else {
		return i2c_msg.tx_write;
	}
}

int BrgHdmiMipiI2cWrite32(uint16_t reg_addr, uint32_t reg_val)
{
	struct i2c_msg_t i2c_msg;
	int ret;
	uint8_t buf[6];

	buf[0] = reg_addr >> 8;
	buf[1] = reg_addr;
	buf[2] = reg_val;
	buf[3] = reg_val >> 8;
	buf[4] = reg_val >> 16;
	buf[5] = reg_val >> 24;

	i2c_msg.addr = BRG_HDMI_MIPI_I2C_ADDR;
	i2c_msg.tx_buf = buf;
	i2c_msg.tx_to_write = 6;
	i2c_msg.tx_write = 0;
	i2c_msg.rx_buf = NULL;
	i2c_msg.rx_to_read = 0;
	i2c_msg.rx_read = 0;

	ret = I2c0Xfer(&i2c_msg);
	if (ret < 0) {
		return 0;
	} else {
		return i2c_msg.tx_write;
	}
}

int BrgHdmiMipiI2cRead(uint16_t reg_addr, uint8_t *reg_val, uint32_t n)
{
	struct i2c_msg_t i2c_msg;
	int ret;
	uint8_t buf[2];

	buf[0] = reg_addr >> 8;
	buf[1] = reg_addr;

	i2c_msg.addr = BRG_HDMI_MIPI_I2C_ADDR;
	i2c_msg.tx_buf = buf;
	i2c_msg.tx_to_write = 2;
	i2c_msg.tx_write = 0;
	i2c_msg.rx_buf = reg_val;
	i2c_msg.rx_to_read = n;
	i2c_msg.rx_read = 0;

	ret = I2c0Xfer(&i2c_msg);
	if (ret < 0) {
		return 0;
	} else {
		return i2c_msg.rx_read;
	}
}

int BrgHdmiMipiI2cRead8(uint16_t reg_addr, uint8_t *reg_val)
{
	return BrgHdmiMipiI2cRead(reg_addr, reg_val, 1);
}

void BrgHdmiMipiInit(void)
{
	BRG_HDMI_MIPI_AUDIO_PIN = 0;
	
	BRG_HDMI_MIPI_RST_PIN = 1;
	GPIO_SetMode(BRG_HDMI_MIPI_INT_PORT, BRG_HDMI_MIPI_INT_BIT, GPIO_PMD_OUTPUT);
	BRG_HDMI_MIPI_INT_PIN = 0;
	DelayMs(10);

	BRG_HDMI_MIPI_RST_PIN = 0;
	DelayMs(10);

	GPIO_SetMode(BRG_HDMI_MIPI_INT_PORT, BRG_HDMI_MIPI_INT_BIT, GPIO_PMD_INPUT);
}

uint32_t BrgHdmiMipiIsHdmiActive(void)
{
	uint8_t reg_val;
	int ret;
	// HDMI Signal Detection
	// Wait until HDMI sync is established
	// By Polling
	ret = BrgHdmiMipiI2cRead8(0x8520, &reg_val); // SYS_STATUS
	if (ret == 0) {
		return 0;
	}
	// Sequence: Check bit7 of 8x8520
	return (reg_val & (0x01 << 7));
}

void BrgHdmiMipiSetVideoTiming(enum video_timing_t timing)
{
	int i;

	if (timing == VIDEO_TIMING_1440P) {
		// Initialization to receive HDMI signal
		// Software Reset
		BrgHdmiMipiI2cWrite16(0x0004,0x0004); // ConfCtl0
		BrgHdmiMipiI2cWrite16(0x0002,0x3F01); // SysCtl
		BrgHdmiMipiI2cWrite16(0x0002,0x0000); // SysCtl
		BrgHdmiMipiI2cWrite16(0x0006,0x0008); // ConfCtl1
		// DSI-TX0 Transition Timing
		BrgHdmiMipiI2cWrite32(0x0108,0x00000001); // DSI_TX_CLKEN
		BrgHdmiMipiI2cWrite32(0x010C,0x00000001); // DSI_TX_CLKSEL
		BrgHdmiMipiI2cWrite32(0x02A0,0x00000001); // MIPI_PLL_CONTROL
		BrgHdmiMipiI2cWrite32(0x02AC,0x00005071); // MIPI_PLL_CNF
		DelayMs(1);
		BrgHdmiMipiI2cWrite32(0x02A0,0x00000003); // MIPI_PLL_CONTROL
		BrgHdmiMipiI2cWrite32(0x0118,0x00000014); // LANE_ENABLE
		BrgHdmiMipiI2cWrite32(0x0120,0x00001770); // LINE_INIT_COUNT
		BrgHdmiMipiI2cWrite32(0x0124,0x00000000); // HSTX_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x0128,0x00000101); // FUNC_ENABLE
		BrgHdmiMipiI2cWrite32(0x0130,0x00010000); // DSI_TATO_COUNT
		BrgHdmiMipiI2cWrite32(0x0134,0x00005000); // DSI_PRESP_BTA_COUNT
		BrgHdmiMipiI2cWrite32(0x0138,0x00010000); // DSI_PRESP_LPR_COUNT
		BrgHdmiMipiI2cWrite32(0x013C,0x00010000); // DSI_PRESP_LPW_COUNT
		BrgHdmiMipiI2cWrite32(0x0140,0x00010000); // DSI_PRESP_HSR_COUNT
		BrgHdmiMipiI2cWrite32(0x0144,0x00010000); // DSI_PRESP_HSW_COUNT
		BrgHdmiMipiI2cWrite32(0x0148,0x00001000); // DSI_PR_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x014C,0x00010000); // DSI_LRX-H_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x0150,0x00000141); // FUNC_MODE
		BrgHdmiMipiI2cWrite32(0x0154,0x00000001); // DSI_RX_VC_ENABLE
		BrgHdmiMipiI2cWrite32(0x0158,0x000000C8); // IND_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x0168,0x0000002A); // DSI_HSYNC_STOP_COUNT
		BrgHdmiMipiI2cWrite32(0x0170,0x0000029D); // APF_VDELAYCNT
		BrgHdmiMipiI2cWrite32(0x017C,0x00000081); // DSI_TX_MODE
		BrgHdmiMipiI2cWrite32(0x018C,0x00000001); // DSI_HSYNC_WIDTH
		BrgHdmiMipiI2cWrite32(0x0190,0x000000EA); // DSI_HBPR
		BrgHdmiMipiI2cWrite32(0x01A4,0x00000000); // DSI_RX_STATE_INT_MASK
		BrgHdmiMipiI2cWrite32(0x01C0,0x00000015); // DSI_LPRX_THRESH_COUNT
		BrgHdmiMipiI2cWrite32(0x0214,0x00000000); // APP_SIDE_ERR_INT_MASK
		BrgHdmiMipiI2cWrite32(0x021C,0x00000080); // DSI_RX_ERR_INT_MASK
		BrgHdmiMipiI2cWrite32(0x0224,0x00000000); // DSI_LPTX_INT_MASK
		BrgHdmiMipiI2cWrite32(0x0254,0x00000007); // LPTXTIMECNT
		BrgHdmiMipiI2cWrite32(0x0258,0x00220006); // TCLK_HEADERCNT
		BrgHdmiMipiI2cWrite32(0x025C,0x000C0006); // TCLK_TRAILCNT
		BrgHdmiMipiI2cWrite32(0x0260,0x000C0006); // THS_HEADERCNT
		BrgHdmiMipiI2cWrite32(0x0264,0x00003E80); // TWAKEUPCNT
		BrgHdmiMipiI2cWrite32(0x0268,0x0000000C); // TCLK_POSTCNT
		BrgHdmiMipiI2cWrite32(0x026C,0x000C0008); // THS_TRAILCNT
		BrgHdmiMipiI2cWrite32(0x0270,0x00000020); // HSTXVREGCNT
		BrgHdmiMipiI2cWrite32(0x0274,0x0000001F); // HSTXVREGEN
		BrgHdmiMipiI2cWrite32(0x0278,0x00070007); // BTA_COUNT
		BrgHdmiMipiI2cWrite32(0x027C,0x00000002); // DPHY_TX ADJUST
		BrgHdmiMipiI2cWrite32(0x011C,0x00000001); // DSITX_START
		// DSI-TX1 Transition Timing
		BrgHdmiMipiI2cWrite32(0x0308,0x00000001); // DSI_TX_CLKEN
		BrgHdmiMipiI2cWrite32(0x030C,0x00000001); // DSI_TX_CLKSEL
		BrgHdmiMipiI2cWrite32(0x04A0,0x00000001); // MIPI_PLL_CONTROL
		BrgHdmiMipiI2cWrite32(0x04AC,0x00005071); // MIPI_PLL_CNF
		DelayMs(1);
		BrgHdmiMipiI2cWrite32(0x04A0,0x00000003); // MIPI_PLL_CONTROL
		BrgHdmiMipiI2cWrite32(0x0318,0x00000014); // LANE_ENABLE
		BrgHdmiMipiI2cWrite32(0x0320,0x00001770); // LINE_INIT_COUNT
		BrgHdmiMipiI2cWrite32(0x0324,0x00000000); // HSTX_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x0328,0x00000101); // FUNC_ENABLE
		BrgHdmiMipiI2cWrite32(0x0330,0x00010000); // DSI_TATO_COUNT
		BrgHdmiMipiI2cWrite32(0x0334,0x00005000); // DSI_PRESP_BTA_COUNT
		BrgHdmiMipiI2cWrite32(0x0338,0x00010000); // DSI_PRESP_LPR_COUNT
		BrgHdmiMipiI2cWrite32(0x033C,0x00010000); // DSI_PRESP_LPW_COUNT
		BrgHdmiMipiI2cWrite32(0x0340,0x00010000); // DSI_PRESP_HSR_COUNT
		BrgHdmiMipiI2cWrite32(0x0344,0x00010000); // DSI_PRESP_HSW_COUNT
		BrgHdmiMipiI2cWrite32(0x0348,0x00001000); // DSI_PR_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x034C,0x00010000); // DSI_LRX-H_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x0350,0x00000141); // FUNC_MODE
		BrgHdmiMipiI2cWrite32(0x0354,0x00000001); // DSI_RX_VC_ENABLE
		BrgHdmiMipiI2cWrite32(0x0358,0x000000C8); // IND_TO_COUNT
		BrgHdmiMipiI2cWrite32(0x0368,0x0000002A); // DSI_HSYNC_STOP_COUNT
		BrgHdmiMipiI2cWrite32(0x0370,0x0000029D); // APF_VDELAYCNT
		BrgHdmiMipiI2cWrite32(0x037C,0x00000081); // DSI_TX_MODE
		BrgHdmiMipiI2cWrite32(0x038C,0x00000001); // DSI_HSYNC_WIDTH
		BrgHdmiMipiI2cWrite32(0x0390,0x000000EA); // DSI_HBPR
		BrgHdmiMipiI2cWrite32(0x03A4,0x00000000); // DSI_RX_STATE_INT_MASK
		BrgHdmiMipiI2cWrite32(0x03C0,0x00000015); // DSI_LPRX_THRESH_COUNT
		BrgHdmiMipiI2cWrite32(0x0414,0x00000000); // APP_SIDE_ERR_INT_MASK
		BrgHdmiMipiI2cWrite32(0x041C,0x00000080); // DSI_RX_ERR_INT_MASK
		BrgHdmiMipiI2cWrite32(0x0424,0x00000000); // DSI_LPTX_INT_MASK
		BrgHdmiMipiI2cWrite32(0x0454,0x00000007); // LPTXTIMECNT
		BrgHdmiMipiI2cWrite32(0x0458,0x00220006); // TCLK_HEADERCNT
		BrgHdmiMipiI2cWrite32(0x045C,0x000C0006); // TCLK_TRAILCNT
		BrgHdmiMipiI2cWrite32(0x0460,0x000C0006); // THS_HEADERCNT
		BrgHdmiMipiI2cWrite32(0x0464,0x00003E80); // TWAKEUPCNT
		BrgHdmiMipiI2cWrite32(0x0468,0x0000000C); // TCLK_POSTCNT
		BrgHdmiMipiI2cWrite32(0x046C,0x000C0008); // THS_TRAILCNT
		BrgHdmiMipiI2cWrite32(0x0470,0x00000020); // HSTXVREGCNT
		BrgHdmiMipiI2cWrite32(0x0474,0x0000001F); // HSTXVREGEN
		BrgHdmiMipiI2cWrite32(0x0478,0x00070007); // BTA_COUNT
		BrgHdmiMipiI2cWrite32(0x047C,0x00000002); // DPHY_TX ADJUST
		BrgHdmiMipiI2cWrite32(0x031C,0x00000001); // DSITX_START
		// Command Transmission Before Video Start
		BrgHdmiMipiI2cWrite16(0x0500,0x0004); // CMD_SEL
		BrgHdmiMipiI2cWrite32(0x0110,0x00000016); // MODE_CONFIG
		BrgHdmiMipiI2cWrite32(0x0310,0x00000016); // MODE_CONFIG
		// LCD Initialization

		// Split Control
		BrgHdmiMipiI2cWrite16(0x5000,0x0000); // STX0_CTL
		BrgHdmiMipiI2cWrite16(0x500C,0x84E0); // STX0_FPX
		BrgHdmiMipiI2cWrite16(0x5080,0x0000); // STX1_CTL
		// HDMI PHY
		BrgHdmiMipiI2cWrite8(0x8410,0x03); // PHY CTL
		BrgHdmiMipiI2cWrite8(0x8413,0x3F); // PHY_ENB
		BrgHdmiMipiI2cWrite8(0x8420,0x07); // EQ_BYPS
		BrgHdmiMipiI2cWrite8(0x84F0,0x31); // APLL_CTL
		BrgHdmiMipiI2cWrite8(0x84F4,0x01); // DDCIO_CTL
		// HDMI Clock
		BrgHdmiMipiI2cWrite16(0x8540,0x12C0); // SYS_FREQ0_1
		BrgHdmiMipiI2cWrite8(0x8630,0x00); // LOCKDET_FREQ0
		BrgHdmiMipiI2cWrite16(0x8631,0x0753); // LOCKDET_REF1_2
		BrgHdmiMipiI2cWrite8(0x8670,0x02); // NCO_F0_MOD
		BrgHdmiMipiI2cWrite16(0x8A0C,0x12C0); // CSC_SCLK0_1
		// HDMI Interrupt Mask, Clear
		BrgHdmiMipiI2cWrite8(0x850B,0xFF); // MISC_INT
		BrgHdmiMipiI2cWrite8(0x851B,0xFD); // MISC_INTM
		// Interrupt Control (TOP level)
		BrgHdmiMipiI2cWrite16(0x0014,0x0FBF); // IntStatus
		BrgHdmiMipiI2cWrite16(0x0016,0x0DBF); // IntMask
		// EDID
		BrgHdmiMipiI2cWrite8(0x85E0,0x01); // EDID_MODE
		BrgHdmiMipiI2cWrite16(0x85E3,0x0100); // EDID_LEN1_2
		// EDID Data
		for (i = 0; i < sizeof(edid_data_2K); i++) {
			BrgHdmiMipiI2cWrite8(0x8C00 + i, edid_data_2K[i]);// EDID Data
		}
		// HDCP Setting
		BrgHdmiMipiI2cWrite8(0x85EC,0x01); // 
		BrgHdmiMipiI2cWrite8(0x8560,0x24); // HDCP_MODE
		BrgHdmiMipiI2cWrite8(0x8563,0x11); // 
		BrgHdmiMipiI2cWrite8(0x8564,0x0F); // 
		// Video Color Format Setting
		BrgHdmiMipiI2cWrite8(0x8A02,0x42); // VOUT_SYNC0
		// HDMI SYSTEM
		BrgHdmiMipiI2cWrite8(0x8543,0x02); // DDC_CTL
		BrgHdmiMipiI2cWrite8(0x8544,0x10); // HPD_CTL
		// HDMI Audio Setting
		BrgHdmiMipiI2cWrite8(0x8600,0x00); // AUD_Auto_Mute
		BrgHdmiMipiI2cWrite8(0x8602,0xF3); // Auto_CMD0
		BrgHdmiMipiI2cWrite8(0x8603,0x02); // Auto_CMD1
		BrgHdmiMipiI2cWrite8(0x8604,0x0C); // Auto_CMD2
		BrgHdmiMipiI2cWrite8(0x8606,0x05); // BUFINIT_START
		BrgHdmiMipiI2cWrite8(0x8607,0x00); // FS_MUTE
		BrgHdmiMipiI2cWrite8(0x8652,0x02); // SDO_MODE1
		BrgHdmiMipiI2cWrite32(0x8671,0x020C49BA); // NCO_48F0A_D
		BrgHdmiMipiI2cWrite32(0x8675,0x01E1B089); // NCO_44F0A_D
		BrgHdmiMipiI2cWrite8(0x8680,0x00); // AUD_MODE
		// Let HDMI Source start access
		BrgHdmiMipiI2cWrite8(0x854A,0x01); // INIT_END

		//for (i = 0; i < sizeof(edid_data_2K); i++) {
		//	BrgHdmiMipiI2cWrite8(0x8C00 + i, edid_data_2K[i]);// EDID Data
		//}
	}
}

void BrgHdmiMipiSetMipiOn(void)
{
	// Start Video TX
	BrgHdmiMipiI2cWrite16(0x0004,0x0C37); // ConfCtl0
	BrgHdmiMipiI2cWrite16(0x0006,0x0000); // ConfCtl1
	// Command Transmission After Video Start.
	BrgHdmiMipiI2cWrite32(0x0110,0x00000006); // MODE_CONFIG
	BrgHdmiMipiI2cWrite32(0x0310,0x00000006); // MODE_CONFIG
}

void BrgHdmiMipiSetAudioOn(void)
{
	BRG_HDMI_MIPI_AUDIO_PIN = 1;
}

void BrgHdmiMipiSetAudioOff(void)
{
	BRG_HDMI_MIPI_AUDIO_PIN = 0;
}

void BrgHdmiMipiDumpRegs(void)
{
	int i;
	uint8_t reg_val;
	uint16_t reg_addrs[] = {
		0x8405,
		0x8406,

		0x858E,
		0x858F,
		0x8580,
		0x8581,
		0x8582,
		0x8583,

		0x8590,
		0x8591,
		0x8584,
		0x8585,
		0x8586,
		0x8587,
		0x858C,
		0x858D,
	
		0x8526,
	};

	for (i = 0; i < sizeof(reg_addrs) / sizeof(reg_addrs[0]); i++) {
		BrgHdmiMipiI2cRead8(reg_addrs[i], &reg_val);
		LogDebug("BrgHdmiMipiDumpRegs : 0x%04X -> 0x%02X\n", reg_addrs[i], reg_val);
	}
}
